package com.ruoyi.flow.auth.security.sso;

import com.ruoyi.flow.auth.security.FlowAuthenticationProvider;
import com.ruoyi.flow.auth.security.sso.SsoRequestContext;
import com.ruoyi.flow.auth.security.sso.SsoUsernamePasswordProvider;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.GenericFilterBean;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.regex.Pattern;

/**
 * SSO登录认证拦截器
 *
 * @author wtian
 * @version 1.0
 * @since 2018/9/12 11:00
 */
@Component
public class SsoAuthenticationFilter extends GenericFilterBean implements ApplicationContextAware {
    private FlowAuthenticationProvider provider;
    private com.ruoyi.flow.auth.security.sso.SsoRequestContext ssoRequestContext;

    // 静态资源文件(css,js,图片等)匹配正则
    private static final Pattern STATIC_ASSETS_PETTERN = Pattern.compile("^(.*)\\.(css|js|png|gif|jpg|jpeg|ico|ttf)$", Pattern.CASE_INSENSITIVE);
    private static final String SESSION_KEY = "SPRING_SECURITY_CONTEXT";
    private String login_ignore_urls;
    // 忽略登录拦截的url地址
    private List<String> loginIgnoreUrls = new ArrayList<String>();
    private ApplicationContext applicationContext;


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    protected void initFilterBean() throws ServletException {
        FilterConfig filterConfig = super.getFilterConfig();
        setLoginIgnoreUrls(this.login_ignore_urls);
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        String path = request.getServletPath();

        if (StringUtils.isEmpty(path)) {
            path = "/";
        }

        if (request.getPathInfo() != null) {
            path = path + request.getPathInfo();
        }

        // 如果是静态资源文件访问的URL，直接放行
        if (isStaticAssets(path)) {
            filterChain.doFilter(request, response);
            return;
        }

        HttpSession session = request.getSession();
        SecurityContext context = session == null ? null : (SecurityContext) session.getAttribute(SESSION_KEY);
        if (Objects.isNull(context) || StringUtils.isEmpty(SecurityUtils.getCurrentUserId())) {//没有登录
            // 只对单点登录进行校验
            ssoRequestContext.init(request);
            SsoUsernamePasswordProvider usernamePasswordProvider = ssoRequestContext.get();
            if (usernamePasswordProvider != null) {//需要校验
                context = usernamePasswordProvider.authenticate(this.provider, request.getParameterMap());
                session.setAttribute(SESSION_KEY, SecurityContextHolder.getContext());
            }
        }
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {

    }

    /**
     * 判断一个请求资源是否是静态资源文件：.css,.js,.png等
     *
     * @param url
     * @return
     */
    private static boolean isStaticAssets(String url) {
        return (url != null && STATIC_ASSETS_PETTERN.matcher(url).find());
    }

    /**
     * 解析配置的忽略登录拦截的URL地址
     *
     * @param ignoreUrls
     */
    private void setLoginIgnoreUrls(String ignoreUrls) {
        if (ignoreUrls == null) {
            return;
        }

        // 多个url地址是使用逗号(,)分隔的
        String[] urls = ignoreUrls.split(",");
        for (String url : urls) {
            if (url.trim().length() == 0) {
                continue;
            }

            // 支持*通配符正则匹配配置
            this.loginIgnoreUrls.add(url.trim().replaceAll("\\.", "\\\\.").replaceAll("\\*", "(.*)"));
        }
    }

    /**
     * 判断一个URL路径是否不需要进行登录权限的拦截
     *
     * @param url 待判断的URL访问路径
     * @return 忽略返回true，否则返回false
     */
    private boolean isLoginIgnoreUrl(String url) {
        for (String igUrl : this.loginIgnoreUrls) {
            if (url.matches(igUrl)) {
                return true;
            }
        }

        return false;
    }

    public com.ruoyi.flow.auth.security.sso.SsoRequestContext getSsoRequestContext() {
        return ssoRequestContext;
    }

    public void setSsoRequestContext(SsoRequestContext ssoRequestContext) {
        this.ssoRequestContext = ssoRequestContext;
    }

    public String getLogin_ignore_urls() {
        return login_ignore_urls;
    }

    public void setLogin_ignore_urls(String login_ignore_urls) {
        this.login_ignore_urls = login_ignore_urls;
    }

    public FlowAuthenticationProvider getProvider() {
        return provider;
    }

    public void setProvider(FlowAuthenticationProvider provider) {
        this.provider = provider;
    }
}
